#include <QtCore>
#include <QtGui>
#include <QtNetwork>

#include "mixiVoice.h"
#include "ui_mixiVoice.h"
#include "CookieJar.h"
#include "SettingDialog.h"

const QString Setting::EMail = "Account/E-Mail";
const QString Setting::Password = "Account/Password";
const QString Setting::SavePassword = "Account/SavePassword";
const QString Setting::ReloadInterval = "General/ReloadInterval";
const QString Setting::AlwaysOnTop = "General/AlwaysOnTop";
const QString Setting::NotificationShowTime = "General/NotificationShowTime";
const QString Setting::WindowSize = "Window/Size";
const QString Setting::WindowPosition = "Window/Position";
const QString Setting::VoiceFormat = "Voice/Format";
const QString Setting::WindowBackgroundColor = "Window/BackgroundColor";
const QString Setting::WindowTextColor = "Window/TextColor";
const QString Setting::MaxVoiceCount = "Voice/MaxCount";

mixiVoice::mixiVoice(QWidget *parent)
	: QWidget(parent), ui(new Ui::mixiVoice)
{
	if (QFileInfo(qApp->applicationName() + ".ini").exists())
		settings = new QSettings(qApp->applicationName() + ".ini", QSettings::IniFormat, this);
	else
		settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName(), this);

	setupContextMenu();
	setupUI();

	// setup network
	const QString appDataPath = QString("%1/%2/").arg(QFileInfo(QSettings(QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName(), this).fileName()).path()).arg(qApp->applicationName());
	QDir().mkdir(appDataPath);
	cookieJar = new CookieJar(appDataPath + "cookie.bin");
	netMan = new QNetworkAccessManager(this);
	connect(netMan, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
	netMan->setCookieJar(cookieJar);

	// interval timer
	timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(reload()));

	// first run
	if (settings->value(Setting::EMail).toString().isEmpty())
		this->showSettingDialog();
	else
	{
		loadSettings();
		reload();
	}
}

void mixiVoice::setupUI()
{
	ui->setupUi(this);

	for (int i = 0; i != (sizeof voice / sizeof voice[0]); ++i)
	{
		QLabel *label = new QLabel(this);
		label->setWordWrap(true);
		label->hide();
		voice[i] = label;
		this->layout()->addWidget(label);
	}

	// system tray icon
	setupContextMenu();
	trayIcon = new QSystemTrayIcon(this);
	trayIcon->setContextMenu(trayIconMenu);
	trayIcon->setIcon(QIcon(":/Icon/Default"));
	trayIcon->setToolTip("mixiVoice");
	trayIcon->setVisible(true);
	connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(show()));
	connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
}

mixiVoice::~mixiVoice()
{
	saveSettings();
	delete ui;
}

void mixiVoice::loadSettings()
{
	if (settings->value("General/AlwaysOnTop").toBool())
		setWindowFlags(Qt::Tool | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
	else
		setWindowFlags(Qt::Widget | Qt::WindowCloseButtonHint);

	maxVoiceCount = settings->value(Setting::MaxVoiceCount, 10).toInt();
	if (maxVoiceCount < 1)	maxVoiceCount = 1;
	if ((sizeof voice / sizeof voice[0]) < maxVoiceCount)	maxVoiceCount = 25;

	this->move(settings->value(Setting::WindowPosition, QPoint(this->x(), this->y())).toPoint());
	this->resize(settings->value(Setting::WindowSize, this->size()).toSize());

	QPalette palette;
	palette.setColor(QPalette::Window, QColor(settings->value(Setting::WindowBackgroundColor, "lightgray").toString()));
	palette.setColor(QPalette::WindowText, QColor(settings->value(Setting::WindowTextColor, "black").toString()));
	setPalette(palette);

	timer->stop();
	timer->setInterval(settings->value(Setting::ReloadInterval, 5).toInt() * 60 * 1000);
}

void mixiVoice::saveSettings()
{
	settings->setValue(Setting::WindowSize, this->size());
	settings->setValue(Setting::WindowPosition, QPoint(this->x(), this->y()));
}

void mixiVoice::closeEvent(QCloseEvent *e)
{
	hide();
	e->ignore();
}

void mixiVoice::setupContextMenu()
{
	trayIconMenu = new QMenu(this);

	QAction *a;

	a = new QAction(tr("Show &Window"), this);
	connect(a, SIGNAL(triggered()), this, SLOT(showNormal()));
	trayIconMenu->addAction(a);

	a = new QAction(tr("&Reload"), this);
	connect(a, SIGNAL(triggered()), this, SLOT(reload()));
	trayIconMenu->addAction(a);

	trayIconMenu->addSeparator();

	a = new QAction(tr("&About"), this);
	connect(a, SIGNAL(triggered()), this, SLOT(about()));
	trayIconMenu->addAction(a);

	trayIconMenu->addSeparator();

	a = new QAction(tr("&Settings"), this);
	connect(a, SIGNAL(triggered()), this, SLOT(showSettingDialog()));
	trayIconMenu->addAction(a);

	a = new QAction(tr("&Quit"), this);
	connect(a, SIGNAL(triggered()), qApp, SLOT(quit()));
	trayIconMenu->addAction(a);
}

void mixiVoice::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
	switch (reason)
	{
/*
		case QSystemTrayIcon::Trigger:
			reload();
			break;
*/

		case QSystemTrayIcon::DoubleClick:
			if (this->isVisible())
				hide();
			else
				show();
			break;

		default:
			break;
	}
}

void mixiVoice::login()
{
	if (settings->value(Setting::Password, QString()).toString().isEmpty())	return;
	cookieJar->clear();
	const QString email = settings->value(Setting::EMail).toString().replace('@', "%40");
	const QString password = settings->value(Setting::Password).toString();
	const QByteArray postData(QString("next_url=/home.pl&email=%1&password=%2&sticky=sticky").arg(email).arg(password).toAscii());
	netMan->post(QNetworkRequest(QUrl("http://mixi.jp/login.pl")), postData);
	if (settings->value(Setting::SavePassword, false).toBool() == false)	settings->setValue(Setting::Password, QString());
}

void mixiVoice::reload()
{
	netMan->get(QNetworkRequest(QUrl("http://mixi.jp/recent_echo.pl")));
	timer->stop();
	timer->start();
}

void mixiVoice::showSettingDialog()
{
	SettingDialog d(settings, this);
	if (d.exec() == QDialog::Rejected)	return;
	saveSettings();
	loadSettings();
	show();
	login();
	reload();
}

void mixiVoice::replyFinished(QNetworkReply *r)
{
	const QString url = r->url().toString();
	// qDebug() << "Pasing..." << url;

	if (url.endsWith("login.pl"))	// login
	{
		if (r->readAll().contains("refresh"))
		{
			trayIcon->showMessage(tr("mixiVoice"), tr("Login Successful"), QSystemTrayIcon::Information, 5 * 1000);
			reload();
		}
		else
		{
			QMessageBox::warning(this, tr("mixi Login"), tr("Failed to login.\nPlease check your e-mail address and password."));
		}
		return;
	}
	else if (url.endsWith(".jpg"))	// user pictures
	{
		QRegExp re("http://profile\\.img\\.mixi\\.jp/photo/member/\\d+/\\d+/(\\d+)_\\d+m\\.jpg");
		if (re.indexIn(url, 0) == -1)	return;
		const QString id = re.cap(1);
		QFile file(QString("%1/%2.jpg").arg(QDir::tempPath()).arg(id));
		if (file.open(QIODevice::WriteOnly))
			file.write(r->readAll());
		else
			qDebug() << "Failed to open file.";
		if (--loadingImage == 0)	reload();	// reload when all user pictures are saved
		return;
	}
	else if (url.endsWith(".gif"))
	{
		return;
	}

	/* parse voices */
	QString content = QTextCodec::codecForName("EUC-JP")->toUnicode(r->readAll()).replace('\n', "");

	QRegExp re("<tr class=\"\\w+\"><td class=\"thumb\"><a href=\"list_echo\\.pl\\?id=(\\d+)\"><img src=\"([^\"]+)\" alt=\"([^\"]+)\" /></a></td><td class=\"nickname\"><a href=\"list_echo\\.pl\\?id=\\d+\">[^<]+</a></td><td class=\"comment\">((<a href=\"view_echo\\.pl\\?from=nickname&id=\\d+&post_time=\\d+\">[^<]+</a>)?[^<]+)<span><a href=\"view_echo\\.pl\\?id=\\d+&post_time=\\d+\">[^<]+</a></span><a href=\\\"javascript:void\\(0\\)\" class=\"echo_reply_click\"><img src=\"[^\"]+\" alt=\"[^\"]+\" title=\"[^\"]+\" /></a>(<a href=\"[^\"]+\" class=\"trash\"><img src=\"[^\"]+\" alt=\"[^\"]+\" title=\"[^\"]+\" /></a>)?<div class=\"echo_member_id\" style=\"display: none;\">\\d+</div><div class=\"echo_post_time\" style=\"display: none;\">\\d+</div><div class=\"echo_nickname\" style=\"display: none;\">[^<]+</div><div class=\"echo_body\" style=\"display: none;\">[^<]+</div></td></tr>");
	QList<Voice> voices;

	for (int i = loadingImage = 0; (i = re.indexIn(content, i)) != -1; i += re.matchedLength())
	{
		int id = re.cap(1).toInt();	// ID
		const QString picURL = re.cap(2);	// Picture
		const QString name = re.cap(3);	// Name
		const QString comment = re.cap(4).replace(QRegExp("<a href=\"[^\"]+\">"), "").replace("</a>", " ").replace("&gt;", ">");	// Comment

		voices.push_back(Voice(id, name, comment));

		/* request user picture */
		if (picURL.endsWith(".jpg") && !QFileInfo(QString("%1/%2.jpg").arg(QDir::tempPath()).arg(id)).exists())
		{
			netMan->get(QNetworkRequest(QUrl(picURL)));
			++loadingImage;
		}
	}

	this->setVoices(voices);
}

void mixiVoice::setVoices(const QList<Voice> &list)
{
	const QString format = settings->value(Setting::VoiceFormat, "<img src=\"%1/%2.jpg\" alt=\"%3\" style=\"float:left; clear:left\" /><strong>%3</strong>: %4").toString();

	/* find new voices */
	const int notificationShowTime = settings->value(Setting::NotificationShowTime, 5).toInt();
	if (notificationShowTime != 0 && !voice[0]->text().isEmpty())
	{
		int newVoice = 0;
		for (int i = 0; i != list.size(); ++i)
		{
			const QString content = format.arg(QDir::tempPath()).arg(list[i].userID).arg(list[i].userName).arg(list[i].content);
			if (voice[0]->text() == content)
			{
				newVoice = i;
				break;
			}
		}

		/* generate tray message */
		QString message;
		for (int i = 0; i != newVoice; ++i)
			message += tr("%1: %2\n").arg(list[i].userName).arg(list[i].content);
		if (!message.isEmpty())
			trayIcon->showMessage(tr("New Voices"), message, QSystemTrayIcon::Information, notificationShowTime * 1000);
	}

	/* update view */
	clearAllVoices();
	for (int i = 0; i != maxVoiceCount && i != (sizeof voice / sizeof voice[0]) && i != list.size(); ++i)
	{
		const QString content = format.arg(QDir::tempPath()).arg(list[i].userID).arg(list[i].userName).arg(list[i].content);
		voice[i]->setText(content);
		voice[i]->show();
	}
	this->resize(this->width(), 1);
}

void mixiVoice::clearAllVoices()
{
	for (int i = 0; i != (sizeof voice / sizeof voice[0]); ++i)
	{
		voice[i]->clear();
		voice[i]->hide();
	}
}

void mixiVoice::about()
{
	QString message = tr("%1\nby %2\nVersion: %3\nQt Version: %4\nCompiled with gcc %5.%6.%7 on %8")
					  .arg(qApp->applicationName())
					  .arg(qApp->organizationName())
					  .arg(qApp->applicationVersion())
					  .arg(QT_VERSION_STR)
					  .arg(__GNUC__)
					  .arg(__GNUC_MINOR__)
					  .arg(__GNUC_PATCHLEVEL__)
					  .arg(__DATE__);
	QMessageBox::information(this, tr("About"), message);
}
